package drexel.marshalling;

import java.io.File;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import drexel.model.Actor;
import drexel.model.Cast;
import drexel.model.DirectedBy;
import drexel.model.Director;
import drexel.model.Genre;
import drexel.model.Genres;
import drexel.model.Movie;

public class DOMXMLMarshaller implements Marshaller {
	
	private static String MOVIE = "Movie";
	private static String TITLE = "Title";
	private static String YEAR = "Year";
	private static String DIRECTED_BY = "Directed_By";
	private static String GENRES = "Genres";
	private static String CAST = "Cast";
	private static String LAST_NAME = "LastName";

	public Movie unmarshall(File file) throws UnmarshallingException {
		
		Movie out = null;
		
		try {
			
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			
			dbFactory.setValidating(true);
			
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			
			dBuilder.setErrorHandler(new ErrorHandler() {
				  
				public void fatalError(SAXParseException e) throws SAXException { 
					
				  // System.out.println("Error at " +e.getLineNumber() + " line.");
				  // System.out.println(e.getMessage());
				  
				  throw e;
				}
				  
				public void error(SAXParseException e) throws SAXParseException {
					
				  // System.out.println("Error at " +e.getLineNumber() + " line.");
				  // System.out.println(e.getMessage());
				  
				  throw e;
				}

				public void warning(SAXParseException e) throws SAXException {
					
				  // System.out.println("Error at " +e.getLineNumber() + " line.");
				  // System.out.println(e.getMessage());
				  
				  throw e;
				}
			});
			
			Document doc = dBuilder.parse(file);
			
			doc.getDocumentElement().normalize();
			
			NodeList movieNodList = doc.getElementsByTagName(MOVIE);
			
			if (movieNodList.getLength() == 1 
				&& movieNodList.item(0).getNodeType() == Node.ELEMENT_NODE) {
				
				out = new Movie ();
				
				Element movieElement = (Element) movieNodList.item(0);
				
				NodeList movieChildrenNodes = movieElement.getChildNodes();
				
				for (int i = 0; i < movieChildrenNodes.getLength(); i++) {
					
					Node child = movieChildrenNodes.item(i);
					
					if (child.getNodeName().equalsIgnoreCase(TITLE)) {
						
						out.setTitle(getNodeValue(child));
						
					} else if (child.getNodeName().equalsIgnoreCase(YEAR)) {
						
						out.setYear(getNodeValue(child));
						
					} else if (child.getNodeName().equalsIgnoreCase(DIRECTED_BY)) {
						
						out.setDirectedBy(createDirectedBy(child));
						
					} else if (child.getNodeName().equalsIgnoreCase(GENRES)) {
						
						out.setGenres(createGenres(child));
						
					} else if (child.getNodeName().equalsIgnoreCase(CAST)) {
						
						out.setCast(createCast(child));
					}
				}
				
			} else {
				
				System.out.println ("ERROR: More then 1 Movie element was found or incorrect node type");
			}
			
		} catch (Exception ex) {
			
			if (ex instanceof SAXParseException) {
				
				throw new UnmarshallingException ("SAX Parsing Exception - DTD Violation");
				
			} else {
				
				ex.printStackTrace();
				
				throw new UnmarshallingException ("Unknown Exception");
			}
		}
		
		return out;
	}
	
	private DirectedBy createDirectedBy (Node node) {
		
		DirectedBy out = new DirectedBy();
		
		NodeList children = node.getChildNodes();
		
		for (int i = 0; i < children.getLength(); i++) {
			
			Node directorNode = children.item(i);
			
			if (directorNode.getNodeType() == Node.ELEMENT_NODE) {

				String directorName = getNodeValue(directorNode);
				
				if (directorName != null && directorName.trim().length() > 0) {
					
					Director director = new Director ();
					
					director.setvalue(directorName);
					
					out.getDirector().add(director);
				}
			}
		}
		
		return out;
	}
	
	private Genres createGenres (Node node) {
		
		Genres out = new Genres();
		
		NodeList children = node.getChildNodes();
		
		for (int i = 0; i < children.getLength(); i++) {
			
			Node genreNode = children.item(i);
			
			if (genreNode.getNodeType() == Node.ELEMENT_NODE) {
			
				Genre genre = new Genre ();
				
				genre.setvalue(getNodeValue(genreNode));
				
				out.getGenre().add(genre);
			}
		}
		
		return out;
	}
	
	private Cast createCast (Node node) {
		
		Cast out = new Cast ();
		
		NodeList children = node.getChildNodes();
		
		for (int i = 0; i < children.getLength(); i++) {
			
			Node actorNode = children.item(i);
			
			if (actorNode.getNodeType() == Node.ELEMENT_NODE) {
			
				out.getActor().add(createActor (actorNode));
			}
		}
		
		return out;
	}
	
	private Actor createActor (Node node) {
	
		Actor out = new Actor ();
		
		NodeList children = node.getChildNodes();
		
		for (int i = 0; i < children.getLength(); i++) {
			
			Node child = children.item(i);
			
			if (child.getNodeType() == Node.ELEMENT_NODE) {
			
				if (child.getNodeName().equalsIgnoreCase(LAST_NAME)) {
					
					out.setLastName(getNodeValue(child));
					
				} else {
					
					out.setFirstName(getNodeValue(child));
				}
			}
		}
		
		return out;
	}
	
	private String getNodeValue (Node node) {
		
		String out = "";
		
		if (node.getNodeType() == Node.ELEMENT_NODE) {
			
			Element element = (Element)node;
			
			if (element.getFirstChild() != null) {
				
				out = element.getFirstChild().getNodeValue();
			}
		}
		
		return out;
	}
}
